/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2010.
-------------------------------------------------------------------------
$Id$
$DateTime$
Description: Vehicle part class that spawns an entity and attaches it to the vehicle.

-------------------------------------------------------------------------
History:
- 01:09:2010: Created by Paul Slinger

*************************************************************************/

#include "StdAfx.h"

#include <IViewSystem.h>
#include <IItemSystem.h>
#include <IVehicleSystem.h>
#include <IPhysics.h>
#include <ICryAnimation.h>
#include <IActorSystem.h>
#include <ISound.h>
#include <ISerialize.h>
#include <IAgent.h>

#include "CryAction.h"
#include "Vehicle.h"
#include "VehiclePartEntity.h"
#include "VehicleUtils.h"

//------------------------------------------------------------------------
CVehiclePartEntity::CVehiclePartEntity() : m_id(0), m_entityId(0), m_pHelper(NULL), m_hidden(false), m_entityAttached(false)
{
}

//------------------------------------------------------------------------
CVehiclePartEntity::~CVehiclePartEntity()
{
	DestroyEntity();

	m_pVehicle->UnregisterVehicleEventListener(this);
}

//------------------------------------------------------------------------
bool CVehiclePartEntity::Init(IVehicle *pVehicle, const CVehicleParams &table, IVehiclePart *parent, CVehicle::SPartInitInfo &initInfo, int partType)
{
	if(!CVehiclePartBase::Init(pVehicle, table, parent, initInfo, eVPT_Entity))
	{
		return false;
	}

	if(CVehicleParams params = table.findChild("Entity"))
	{
		params.getAttr("id", m_id);

		m_entityName			= params.getAttr("name");
		m_entityArchetype = params.getAttr("archetype");
		m_helperName			= params.getAttr("helper");
	}

	m_pVehicle->RegisterVehicleEventListener(this, "VehiclePartEntity");

	return true;
}

//------------------------------------------------------------------------
void CVehiclePartEntity::PostInit()
{
	if(!m_helperName.empty())
	{
		m_pHelper = m_pVehicle->GetHelper(m_helperName.c_str());
	}

	Reset();
}

//------------------------------------------------------------------------
void CVehiclePartEntity::Reset()
{
	if(m_entityId && !gEnv->pEntitySystem->GetEntity(m_entityId))
	{
		m_entityId = 0;
	}

	if(!m_entityId)
	{
		SEntitySpawnParams	entitySpawnParams;

		entitySpawnParams.pArchetype = gEnv->pEntitySystem->LoadEntityArchetype(m_entityArchetype.c_str());

		if(entitySpawnParams.pArchetype)
		{
			entitySpawnParams.pClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(m_entityName.c_str());

			if(entitySpawnParams.pClass)
			{
				entitySpawnParams.vPosition		= Vec3(0.0f, 0.0f, 0.0f);
				entitySpawnParams.sName				= m_entityName.c_str();

				if(IEntity *pEntity = gEnv->pEntitySystem->SpawnEntity(entitySpawnParams))
				{
					m_entityId = pEntity->GetId();

					m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_AlwaysUpdate);

					pEntity->Hide(m_hidden);

					UpdateEntity();
				}
			}
		}
	}

	m_entityAttached = !!m_entityId;
}

//------------------------------------------------------------------------
void CVehiclePartEntity::Update(const float frameTime)
{
	CVehiclePartBase::Update(frameTime);

	if(m_entityAttached)
	{
		UpdateEntity();
	}
}

//------------------------------------------------------------------------
void CVehiclePartEntity::OnVehicleEvent(EVehicleEvent event, const SVehicleEventParams &params)
{
	switch(event)
	{
		case eVE_Destroyed:
		{
			DestroyEntity();

			break;
		}

		case eVE_TryDetachPartEntity:
		{
			if(params.iParam == m_id)
			{
				SVehicleEventParams	vehicleEventParams = params;

				vehicleEventParams.entityId	= m_entityId;
				vehicleEventParams.iParam		= m_pVehicle->GetEntityId();

				m_pVehicle->BroadcastVehicleEvent(eVE_OnDetachPartEntity, vehicleEventParams);

				m_entityAttached = false;
			}

			break;
		}

		case eVE_Hide:
		{
			m_hidden = !!params.iParam;

			if(m_entityAttached)
			{
				if(IEntity *pEntity = GetPartEntity())
				{
					pEntity->Hide(m_hidden);
				}
			}

			break;
		}
	}
}

//------------------------------------------------------------------------
void CVehiclePartEntity::Serialize(TSerialize serializer, EEntityAspects aspects)
{
	CVehiclePartBase::Serialize(serializer, aspects);

	uint32	flags = 0;

	if(serializer.IsWriting())
	{
		if(m_hidden)
		{
			flags |= Flags::Hidden;
		}

		if(m_entityAttached)
		{
			flags |= Flags::EntityAttached;
		}
	}

	serializer.Value("entityId", m_entityId);

	serializer.Value("flags", flags);

	if(serializer.IsReading())
	{
		m_hidden					= !!(flags & Flags::Hidden);
		m_entityAttached	= !!(flags & Flags::EntityAttached);
	}
}

//------------------------------------------------------------------------
void CVehiclePartEntity::PostSerialize()
{
	CVehiclePartBase::PostSerialize();

	Reset();
}

//------------------------------------------------------------------------
void CVehiclePartEntity::GetMemoryUsage(ICrySizer *pSizer) const
{
	pSizer->Add(*this);

	CVehiclePartBase::GetMemoryUsageInternal(pSizer); 
}

//------------------------------------------------------------------------
IEntity *CVehiclePartEntity::GetPartEntity() const
{
	return m_entityId ? gEnv->pEntitySystem->GetEntity(m_entityId) : NULL;
}

//------------------------------------------------------------------------
void CVehiclePartEntity::UpdateEntity()
{
	if(IEntity *pEntity = GetPartEntity())
	{
		Matrix34	worldTM;

		if(m_pHelper)
		{
			m_pHelper->GetWorldTM(worldTM);
		}
		else if(IVehiclePart *pParent = GetParent())
		{
			worldTM	= pParent->GetWorldTM();
		}
		else
		{
			worldTM	= GetEntity()->GetWorldTM();
		}

		pEntity->SetWorldTM(worldTM);
	}
}

//------------------------------------------------------------------------
void CVehiclePartEntity::DestroyEntity()
{
	if(m_entityId)
	{
		gEnv->pEntitySystem->RemoveEntity(m_entityId);

		m_entityId = 0;
	}

	m_entityAttached = false;

	m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_NoUpdate);
}

//------------------------------------------------------------------------
DEFINE_VEHICLEOBJECT(CVehiclePartEntity)